package network;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Timer;

import bundle.BundleLayer;
import link.*;

public class BellmanFord {

	PhysicalLayer pl;
	public HashMap<String, DistanceCouple> distances;
	public HashMap<String, ViaCouple> via;
	HashMap<String, ViaCouple> tempVia;
	int maxHops = 20;
	LinkedList<String> resend;
	Timer resendCheck;
	final long TIMERESENDCHECK = 10 * 1000;

	public BellmanFord(PhysicalLayer pl) {
		this.pl = pl;
		distances = new HashMap<String, DistanceCouple>();
		distances.put(NetworkLayer.myID, new DistanceCouple());
		via = new HashMap<String, ViaCouple>();
		resend = new LinkedList<String>();
		updateVia();
		resendCheck = new Timer();
		resendCheck
				.schedule(new ResendTask(), TIMERESENDCHECK, TIMERESENDCHECK);
	}

	public synchronized void showTable() {
		String msg = "ALL:" + NetworkLayer.myID + ":vector:"
				+ via.keySet().size() + ":";
		for (String s : via.keySet()) {
			msg = msg + s + ":" + via.get(s).getNextHop() + ":"
					+ via.get(s).getCost() + ":";
		}
		System.out.println(msg);
	}

	public synchronized void sendMessage() {
		String msg = "ALL:" + NetworkLayer.myID + ":vector:"
				+ via.keySet().size() + ":";
		for (String s : via.keySet()) {
			msg = msg + s + ":" + via.get(s).getNextHop() + ":"
					+ via.get(s).getCost() + ":";
		}
		System.err.println("Routing Table updated.");
		pl.send(msg);
	}

	public synchronized void handleVector(String msg) {
		String[] splitMessage = msg.split(":");
		String source = splitMessage[1];
		int p = Integer.parseInt(splitMessage[3]);
		boolean updateNotNeeded = distances.containsKey(source);
		if (!updateNotNeeded) {
			distances.put(source, new DistanceCouple());
		}
		updateNotNeeded = (p == distances.get(source).distances.size());
		HashMap<String, Integer> map = new HashMap<String, Integer>();
		for (int i = 1; i <= p; i++) {
			// updateNotNeeded = updateNotNeeded &
			// distances.get(source).alterDistances(
			// splitMessage[3 * i + 1],
			// Integer.parseInt(
			// splitMessage[3 * i + 3]));
			if (Integer.parseInt(splitMessage[3 * i + 3]) < maxHops) {
				map.put(splitMessage[3 * i + 1], Integer
						.parseInt(splitMessage[3 * i + 3]));
				updateNotNeeded &= (distances.get(source).distances
						.containsKey(splitMessage[3 * i + 1]) && (map
						.get(splitMessage[3 * i + 1])) == distances.get(source).distances
						.get(splitMessage[3 * i + 1]));
			} else {
				updateNotNeeded &= !distances.get(source).distances
						.containsKey(splitMessage[3 * i + 1]);
			}
		}
		distances.get(source).distances = map;
		if (!updateNotNeeded) {
			updateVia();
		}
	}

	public synchronized void updateVia() {
		tempVia = new HashMap<String, ViaCouple>();
		LinkedList<String> toCheck = new LinkedList<String>();
		for (String s : NetworkLayer.neighbours.neighboursTable.keySet()) {
			if (!s.equals(NetworkLayer.myID)) {
				tempVia.put(s, new ViaCouple(s, 1));
				toCheck.addLast(s);
			} else {
				tempVia.put(s, new ViaCouple(s, 0));
			}
		}
		while (!toCheck.isEmpty()) {
			String current = toCheck.removeFirst();
			if (distances.containsKey(current)) {
				for (String s : distances.get(current).distances.keySet()) {
					if (BundleLayer.warehouse.storage.containsKey(s)) {
						resend.add(s);
					}
					if (!tempVia.containsKey(s)
							|| (tempVia.containsKey(s) && tempVia.get(s).cost > tempVia
									.get(current).cost
									+ distances.get(current).distances.get(s))
							&& !(tempVia.get(current).cost
									+ distances.get(current).distances.get(s) >= maxHops)) {
						toCheck.addLast(s);
						tempVia.put(s, new ViaCouple(tempVia.get(current)
								.getNextHop(), tempVia.get(current).cost
								+ distances.get(current).distances.get(s)));
					}
				}
			}
		}
		if (!tempVia.equals(via)) {
			via = tempVia;
			sendMessage();
		}
	}
}
